/*
 * calculation.cc
 * Perform the calculation using the results from the simulated radio.
 *
 *  Created on: Oct 20, 2014
 *      Author: andy
 */

#include "calculation.h"

/* rotate(base,r)
 * Rotate the radial r relative to base so that base' = 0 in the transformed space.
 * Assumes that base and r are both in [0,360)
 * Returns the transformed r' in (-180,180].
 */
int rotate(int base, int r) {
	// Adjust to base' = 0
	r -= base;

	// Now r is in [-base,360 - base). So we readjust it if r <= 180 || r > 180.
	// Recall that "rotating" by +- 360 does not change the actual radial.
	if(r <= -180) {
		r += 360;
	}
	else if(r > 180) {
		r -= 360;
	}

	return r;
}

/* calculate
 * Calculate the needle displacement, TO/FROM direction, and validity of the display, given
 * the actual and desired radials, and the validity of the
 */
bool calculate(int actual_radial, int desired_radial, bool valid, CalcResults &results) {

	// Note that the main if of this function *only* modifies results. Also note that
	// calculate has a single exit point, the return statement at the end.

	// Our calculation can only handle radials in [0,360). Radials outside this range
	// immediately generate invalid results.
	if(actual_radial < 0 || desired_radial < 0 ||
	   actual_radial >= 360 || desired_radial >= 360) {
		results.valid = false;
	}
	else if(valid) {
		// actual_radial is valid, compute displacement.

		/* We proceed by rotating both actual_radial and desired_radial so that desired_radial
		 * is at 0 degrees, and actual_radial is in the range [-179,180]. In this transformed
		 * space, there are three cases:
		 *
		 *   -  actual_radial < 90 && actual_radial > -90 : In this case, we are in the TO
		 *      range and the value actual_radial gives the displacement.
		 *
		 *   -  actual_radial == +- 90 : In this case, the plane is exactly 90 degrees
		 *      perpendicular to the desired radial, so no valid information can be
		 *      extracted (results.valid = false).
		 *
		 *   -  actual_radial > 90 || actual_radial < -90 : In this case, the plane is on
		 *      the opposite side of the station from the desired radial, indicating we
		 *      have passed over the station into the FROM range. The displacement is
		 *      given by rotating actual_radial 180 degrees and then re-adjusting it to
		 *      the [-179,180] range.
		 */
		actual_radial = rotate(desired_radial, actual_radial);

		if(actual_radial < 90 && actual_radial > -90) {
			// First case, FROM
			results.displacement = -actual_radial;
			results.direction    = CalcResults::FROM;
			results.valid        = true;
		}
		else if(actual_radial == 90 || actual_radial == -90) {
			// Second case, technically invalid. Note that results.valid is
			// true here, because the *signal* is valid. This is useful for
			// testing as it allows us to disambiguate the two invalid cases.
			// We leave the displacement unset, since it's value could
			// arguably be either + or -90; we don't know which.
			results.direction = CalcResults::UNKNOWN;
			results.valid = true;
		}
		else if(actual_radial > 90 || actual_radial < -90) {
			// Third case, TO, rotate 180 degrees.
			results.displacement = rotate(0, 180 + actual_radial);
			results.direction    = CalcResults::TO;
			results.valid        = true;
		}
	}
	else
		results.valid = false;

	return results.valid;
}

/* CalcResults.areResultsValid
 * Check the state of a CalcResults object and return true if it
 * represents the valid results of a calculation (i.e., all data
 * members are in range).
 */
bool CalcResults::areResultsValid() {
	return this->valid ? (this->direction == UNKNOWN || this->direction == TO || this->direction == FROM) &&
			             this->displacement >= -180 && this->displacement <= 180
			           : true ;
}


